#include "stdafx.h"
#include "MovableMover.h"
#include <LitSceneManager.h>
#include <LitCore.h>

MovablePlacer::MovablePlacer( RenderViewportPtr vp ) 
	: mPlacingMode(false)
	, mMovePlane( Lit::PLANE_XZ )
	, mViewport( vp )
{}
MovablePlacer::~MovablePlacer(){}
	
Lit::PlaneAxis MovablePlacer::movingPlane() const{ return mMovePlane; }
void MovablePlacer::setMovePlane( Lit::PlaneAxis plane ){ mMovePlane = plane; }

bool MovablePlacer::isPlacing() const{ return mPlacingMode ; }
void MovablePlacer::setPlacingMode( bool state ){ mPlacingMode = state; }
	
void MovablePlacer::update( )
{
	if( isPlacing() )
	{
		Lit::SceneSelectionPtr sel = Lit::Root::SceneManager()->getSelection();
		Lit::Vec3 offset, final;
		Lit::Vec3 center=sel->center();
		UserQueryPtr q(new UserSceneQuery( mViewport->ogreWidget(), Lit::Root::SceneManager() ));
		UserQueryResult res=q->execute( mViewport->ogreWidget()->ogreCamera(), mViewport->ogreWidget()->mapFromGlobal( QCursor::pos() ) );
		bool found = false;

		// if camera ray collided and is not volume
		if( res.hit && !res.volume )
		{
			Ogre::Vector3 p;
			// find snappable object
			for( auto j=res.contacts.begin(); ((j!=res.contacts.end()) &&!found); j++ )
			{
				bool bfound = false;
				// if contact is from selection
				for( auto i=sel->begin(); i!=sel->end(); i++ )
				{
					if( j->movable == i->get()->movable() ){
						bfound = true;
						break;
					}
				}
				// if found snappable object 
				if( !bfound )
				{
					// snap to object
					if( GetAsyncKeyState(VK_CONTROL) & 0x8000 )
					{
						p = j->movable->getParentSceneNode()->getPosition() + Lit::Vec3( 0.0f, j->movable->getBoundingBox().getHalfSize().y*j->movable->getParentSceneNode()->getScale().y,0.0f);
						// point = (collided movable position) + (collided movable half height);
					}
					else p = j->point;

					Ogre::Vector3 hsize;
					for( auto i=sel->begin(); i!=sel->end(); i++ )
						hsize += i->get()->getBoundingBox().getSize() * i->get()->getScale();
					hsize /= sel->size();
					hsize *= 0.5f;
					for( auto i=sel->begin(); i!=sel->end(); i++ )
					{
						offset = i->get()->getPosition()-center;
						i->get()->setPosition( p+offset+Lit::Vec3( 0.0f, hsize.y, 0.0f ) );
					}
					found = true;
					break;
				}

			}
		}
		q.destroy();
		// snappabled object not found
		// snap to plane
		if( !found )
		{
			Ogre::Vector3 p;
			Ogre::Vector3 hsize;
			for( auto i=sel->begin(); i!=sel->end(); i++ )
				hsize += i->get()->getBoundingBox().getSize() * i->get()->getScale();
			hsize /= sel->size();
			hsize *= 0.5f;
			for( auto i=sel->begin(); i!=sel->end(); i++ )
			{
				Lit::MovablePtr mov(i->get());
				p = getMovablePlace( mViewport->getUserRay(), mMovePlane, /*distanceFromPlane( i->get()->getPosition(), mMoveAxis )*/0.0f );
				offset = mov->getPosition()-center;
				mov->setPosition( p+offset+Lit::Vec3( 0.0f, hsize.y, 0.0f ) );
			}
		}
	}
}
	
Ogre::Real MovablePlacer::distanceFromPlane( const Ogre::Vector3& p, Lit::PlaneAxis plane )
{
	switch( plane )
	{
	case Lit::PLANE_XZ: return p.y;
	case Lit::PLANE_XY: return p.z;
	case Lit::PLANE_YZ: return p.x;
	};
	return 0;
}
void MovablePlacer::updatePlace( Ogre::Ray user_ray, Lit::PlaneAxis axis, Ogre::Real distance )
{
	Lit::SceneManagerPtr mgr = Lit::Root::SceneManager();
	Lit::SceneSelectionPtr sel = mgr->getSelection();
	Lit::Vec3 ctr = sel->center();
	if( mPlacingMode && !sel->empty() )
	{
		Lit::Vec3 p = getMovablePlace( user_ray, axis, distance );
		if( p != Lit::Vec3::ZERO )
		{
			for( auto i=sel->begin(); i!=sel->end(); i++ )
			{
				Ogre::Vector3 offset = i->get()->getPosition() - ctr;
				i->get()->setPosition( p + offset );
			}
		}
	}
}
Ogre::Vector3 MovablePlacer::getMovablePlace( Ogre::Ray ray, Lit::PlaneAxis axis, Ogre::Real distance )
{
	//std::pair<bool, Real> Ogre::Ray::intersects	(	const Plane & p )	 const
	Ogre::Plane plane;
	switch( axis )
	{
	case Lit::PLANE_XZ: plane = Ogre::Plane( Lit::Vec3::UNIT_Y, distance ); break;
	case Lit::PLANE_XY: plane = Ogre::Plane( Lit::Vec3::UNIT_Z, distance ); break;
	case Lit::PLANE_YZ: plane = Ogre::Plane( Lit::Vec3::UNIT_X, distance ); break;
	default:
		throw Lit::ImplementationMissing("MovablePlacer<"+std::string(Lit::trPlaneAxis(axis))+">", EXC_LOC);
	};
	std::pair<bool, Ogre::Real> pair = ray.intersects( plane );
	if( pair.first )
		return ray.getPoint(pair.second);
	return Ogre::Vector3::ZERO;
}